#ifndef _CANARY2_CORO_EPSC_MANAGER_H_
#define _CANARY2_CORO_EPSC_MANAGER_H_

#include <mutex>
#include <vector>
#include <map>
#include <mutex>
#include <shared_mutex>
#include <utility>

#include <sys/epoll.h>
#include <fmt/format.h>

#include "../util/assert.h"

namespace wilson::coro
{
    struct epoll_suspended_context
    {
        uint32_t est_holded_events;
        void*    est_in_coro_handle{ nullptr };
        void*    est_out_coro_handle{ nullptr };
        ::std::mutex est_mutex;

        void add_event(uint32_t events, void* p)
        {
            if (events & EPOLLIN)
                est_in_coro_handle = p;
            else if (events & EPOLLOUT)
                est_out_coro_handle = p;

            WILSON_ASSERT_MSG(est_in_coro_handle != est_out_coro_handle, fmt::format(

                "in should not == out, "
                "coroutine: inptr == {}, outptr == {}"
                "var `events` contians {}{}, "
                "p == {}, "
                "var ex`est_holded_events` contains {}{}", 

                est_in_coro_handle, est_out_coro_handle, 

                (events & EPOLLIN)  ? "EPOLLIN"  : "", 
                (events & EPOLLOUT) ? "EPOLLOUT" : "",

                p, 

                (est_holded_events & EPOLLIN)  ? "EPOLLIN"  : "", 
                (est_holded_events & EPOLLOUT) ? "EPOLLOUT" : ""

            ).c_str());

            est_holded_events |= events;
        }

        auto retrive_related_coro_addr(uint32_t events)
        {
            WILSON_ASSERT(events);
            ::std::pair<void*, void*> ret{};

            if (events & EPOLLIN) 
                ret.first = ::std::exchange(est_in_coro_handle, nullptr);
            if (events & EPOLLOUT)
                ret.second = ::std::exchange(est_out_coro_handle, nullptr);

            est_holded_events &= ~events;

            return ret;
        }

        uint32_t holded_events() const noexcept { return est_holded_events; }

        auto get_lock() { return ::std::unique_lock{ est_mutex }; }
        bool empty() const { return est_holded_events == 0; }
    };
    
    class EPSC_manager
    {
    public:
        EPSC_manager();
        EPSC_manager(size_t init_blk);
        ~EPSC_manager() noexcept;
        epoll_suspended_context* operator[](size_t fd);
        size_t size() const noexcept; 

    private:
        const uint64_t em_init_blk;
        ::std::vector<epoll_suspended_context*> em_ctxs;
        mutable ::std::shared_mutex em_mutex;
    };
}

#endif
